import * as THREE from 'three';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
import { Wireframe } from 'three/examples/jsm/lines/Wireframe.js';
import { WireframeGeometry2 } from 'three/examples/jsm/lines/WireframeGeometry2.js';

import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';

type MaterialMap = {
  [uuid: string]: THREE.Material | THREE.Material[];
};

export default class GlowEffect {
  container: HTMLElement;
  materials: MaterialMap = {};
  BLOOM_LAYER = 1;
  models: THREE.Object3D[];
  Scene: THREE.Scene;
  renderer: THREE.WebGLRenderer;
  renderScene: RenderPass;
  bloomPass: UnrealBloomPass;
  finalComposer: EffectComposer;
  bloomComposer: EffectComposer;
  outputPass: OutputPass;
  matLine: LineMaterial;
  darkMaterial: THREE.MeshBasicMaterial;
  bloomLayer: THREE.Layers;

  constructor(
    models: THREE.Object3D[],
    renderer: THREE.WebGLRenderer,
    scene: THREE.Scene,
    camera: THREE.PerspectiveCamera,
    container: HTMLElement
  ) {
    this.container = container;
    this.Scene = scene;
    this.renderer = renderer;
    this.models = models;
    this.renderScene = new RenderPass(scene, camera);
    this.bloomPass = new UnrealBloomPass(
      new THREE.Vector2(
        this.container.clientWidth,
        this.container.clientHeight
      ),
      0.5,
      0.0,
      0.0
    );
    this.matLine = new LineMaterial({
      color: 0x0080ff,
      linewidth: 3, // in pixels
      resolution: new THREE.Vector2(
        this.container.clientWidth,
        this.container.clientHeight
      ),
      dashed: false,
    });
    this.darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' });
    this.bloomLayer = new THREE.Layers();
    this.bloomLayer.set(this.BLOOM_LAYER);
    this.bloomLayer.set(this.BLOOM_LAYER);
    this.bloomComposer = new EffectComposer(renderer);
    this.bloomComposer.renderToScreen = false;
    this.bloomComposer.addPass(this.renderScene);
    this.bloomComposer.addPass(this.bloomPass);
    this.outputPass = new OutputPass();
    this.finalComposer = new EffectComposer(renderer);
    this.finalComposer = new EffectComposer(renderer);
    this.finalComposer.addPass(this.renderScene);
    this.finalComposer.addPass(this.mixPass());
    this.finalComposer.addPass(this.outputPass);
    this.traverseModel();
  }

  // 创建用于合并基础和辉光纹理的混合 Pass
  mixPass() {
    return new ShaderPass(
      new THREE.ShaderMaterial({
        uniforms: {
          baseTexture: { value: null },
          bloomTexture: { value: this.bloomComposer.renderTarget2.texture },
        },
        vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
        }`,
        fragmentShader: `
        uniform sampler2D baseTexture;
        uniform sampler2D bloomTexture;
        varying vec2 vUv;
        void main() {
          gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
        }`,
        defines: {},
      }),
      'baseTexture'
    );
  }

  //遍历模型
  traverseModel() {
    this.models.forEach((object: THREE.Object3D) => {
      object.traverse((node: THREE.Object3D) => {
        if (node instanceof THREE.Mesh) {
          this.do_wireframe(node);
        }
      });
    });
  }

  // 创建 3D 对象的线框版本并将其添加到场景中
  do_wireframe(node: THREE.Mesh) {
    if (node.geometry) {
      const edges = new THREE.EdgesGeometry(node.geometry);
      let geometry = new WireframeGeometry2(node.geometry);
      geometry = geometry.fromEdgesGeometry(edges);
      const wireFrame = new Wireframe(geometry, this.matLine);
      wireFrame.position.copy(node.position);
      wireFrame.rotation.copy(node.rotation);
      wireFrame.scale.copy(node.scale);
      wireFrame.layers.enable(this.BLOOM_LAYER);
      node.parent?.add(wireFrame);
    }
  }

  // 在场景中使非辉光物体变暗
  darkenNonBloomed(obj: THREE.Mesh) {
    if (obj.isMesh && this.bloomLayer.test(obj.layers) === false) {
      this.materials[obj.uuid] = obj.material;
      obj.material = this.darkMaterial;
    }
  }
  // 恢复物体的原始材质
  restoreMaterial(obj: THREE.Mesh) {
    if (this.materials[obj.uuid]) {
      obj.material = this.materials[obj.uuid];
      delete this.materials[obj.uuid];
    }
  }

  // 执行渲染和后期处理
  animate() {
    this.renderer.setClearColor(0x000000, 1);
    this.matLine.resolution.set(
      this.container.clientWidth,
      this.container.clientHeight
    ); // resolution of the viewport
    // 1. 利用 darkenNonBloomed 函数将除辉光物体外的其他物体的材质转成黑色
    this.Scene.traverse((node: THREE.Object3D) => {
      if (node instanceof THREE.Mesh) {
        this.darkenNonBloomed(node);
      }
    });
    // 2. 用 bloomComposer 产生辉光
    this.bloomComposer.render();
    // 3. 将转成黑色材质的物体还原成初始材质
    this.Scene.traverse((node: THREE.Object3D) => {
      if (node instanceof THREE.Mesh) {
        this.restoreMaterial;
      }
    });
    // 4. 用 finalComposer 作最后渲染
    this.finalComposer.render();
  }
}
